/*
Copyright SecureKey Technologies Inc. All Rights Reserved.

SPDX-License-Identifier: Apache-2.0
*/

package chain

import (
	reqContext "context"
	"time"

	pb "git.cloud.inspur.com/ichain/ichain-protos-go/inode"
	"git.cloud.inspur.com/ichain/ichain-sdk-go/pkg/common/errors/retry"
	"git.cloud.inspur.com/ichain/ichain-sdk-go/pkg/ichain/comm"
	"git.cloud.inspur.com/ichain/ichain-sdk-go/pkg/providers/context"
	"git.cloud.inspur.com/ichain/ichain-sdk-go/pkg/providers/ichain"
	"github.com/pkg/errors"
)

type requestOptions struct {
	Targets       []ichain.Inode                       // 目标节点
	Retry         retry.Opts                           // 重试参数
	BeforeRetry   retry.BeforeRetryHandler             // 重试前处理
	Timeouts      map[ichain.TimeoutType]time.Duration // 超时时间
	ParentContext reqContext.Context
}

type RequestOption func(ctx context.Client, opts *requestOptions) error

type Request struct {
	ContractID      string
	Fcn             string
	Args            [][]byte
	TransientMap    map[string][]byte
	InvocationChain []*ichain.ContractCall
	IsInit          bool
}

type Response struct {
	Proposal         *ichain.TransactionProposal
	Responses        []*ichain.TransactionProposalResponse
	TransactionID    ichain.TransactionID
	TxValidationCode pb.TxValidationCode
	ContractStatus   int32
	Payload          []byte
}

func WithTargets(targets ...ichain.Inode) RequestOption {
	return func(ctx context.Client, o *requestOptions) error {

		for _, t := range targets {
			if t == nil {
				return errors.New("target is nil")
			}
		}

		o.Targets = targets
		return nil
	}
}

func WithTargetEndpoints(keys ...string) RequestOption {
	return func(ctx context.Client, opts *requestOptions) error {

		var targets []ichain.Inode

		for _, url := range keys {

			inodeCfg, err := comm.NetworkInodeConfig(ctx.EndpointConfig(), url)
			if err != nil {
				return err
			}

			inode, err := ctx.IChainProvider().CreateInodeFromConfig(inodeCfg)
			if err != nil {
				return errors.WithMessage(err, "creating inode from config failed")
			}

			targets = append(targets, inode)
		}

		return WithTargets(targets...)(ctx, opts)
	}
}

func WithRetry(retryOpt retry.Opts) RequestOption {
	return func(ctx context.Client, o *requestOptions) error {
		o.Retry = retryOpt
		return nil
	}
}

func WithBeforeRetry(beforeRetry retry.BeforeRetryHandler) RequestOption {
	return func(ctx context.Client, o *requestOptions) error {
		o.BeforeRetry = beforeRetry
		return nil
	}
}

func WithTimeout(timeoutType ichain.TimeoutType, timeout time.Duration) RequestOption {
	return func(ctx context.Client, o *requestOptions) error {
		if o.Timeouts == nil {
			o.Timeouts = make(map[ichain.TimeoutType]time.Duration)
		}
		o.Timeouts[timeoutType] = timeout
		return nil
	}
}

func WithParentContext(parentContext reqContext.Context) RequestOption {
	return func(ctx context.Client, o *requestOptions) error {
		o.ParentContext = parentContext
		return nil
	}
}
